home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Tools - Objects / MacApp / MacApp 2.0 CD Release / MacApp 2.0 (Many Libraries) / Libraries / UTEView.TTECommand.p < prev    next >
Encoding:
Text File  |  1990-03-27  |  30.9 KB  |  1,105 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
  3. {UTEView.TTECommand.p}
  4. {Copyright © 1984-1990 Apple Computer Inc. All rights reserved.}
  5.  
  6. {--------------------------------------------------------------------------------------------------}
  7. {$S TESelCommand}
  8.  
  9. PROCEDURE TTECommand.ITECommand(itsTEView: TTEView;
  10.                                 itsCmdNumber: CmdNumber;
  11.                                 itsSaveText: BOOLEAN);
  12.  
  13.     VAR
  14.         selChars:            INTEGER;
  15.         h:                    Handle;
  16.         fi:                 FailInfo;
  17.  
  18.     PROCEDURE HdlInitFailed(error: OSErr;
  19.                             message: LONGINT);
  20.  
  21.         BEGIN
  22.         Free;
  23.         END;
  24.  
  25.     BEGIN
  26.     fTEView := itsTEView;
  27.     fHTE := itsTEView.fHTE;
  28.  
  29.     WITH fHTE^^ DO
  30.         BEGIN
  31.         fOldStart := selStart;
  32.         fOldEnd := selEnd;
  33.         selChars := selEnd - selStart;
  34.         END;
  35.  
  36.     fOldText := NIL;
  37.     fOldStyles := NIL;
  38.  
  39.     fNewStart := 0;
  40.     fNewEnd := 0;
  41.     fNewText := NIL;
  42.     fNewStyles := NIL;
  43.  
  44.     fPadding := NIL;
  45.     fTextPad := 0;
  46.     fStylePad := 0;
  47.  
  48.     ICommand(itsCmdNumber, itsTEView.fDocument, itsTEView, NIL);
  49.     CatchFailures(fi, HdlInitFailed);
  50.  
  51.     IF itsSaveText THEN
  52.         BEGIN
  53.         h := NewPermHandle(selChars);
  54.         FailNIL(h);
  55.  
  56.         IF selChars > 0 THEN
  57.             BlockMove(Pointer(ORD(fHTE^^.hText^) + fOldStart), h^, selChars);
  58.  
  59.         fOldText := h;
  60.         fTextPad := fOldStart - fOldEnd;
  61.         fPadding := NewPermHandle(0);
  62.         FailNIL(fPadding);
  63.         END;
  64.  
  65.  { TextEdit has this "feature" which it exercises if it runs out of memory.  It's
  66.    called DS number 25.  We'll try to avoid it by assuring that enough memory exists
  67.    to fulfill the request, but we won't die because of it.  This is a particularly
  68.    ugly situation - there could be >600K of style information associated with a 32K
  69.    block of text.  And to support undo, we've got to assume that there may momentarily
  70.    be THREE copies floating around, adding up to a total potential liability of almost
  71.    2 Meg for a single TE record.  The worst that can happen, though, is that the text
  72.    will be safe, but it won't have any styles associated with it. }
  73.  
  74.     IF (itsTEView.fStyleType = kWithStyle) & itsTEView.SpaceForStyles(fHTE^^.selStart,
  75.                                                                       fHTE^^.selEnd) THEN
  76.         BEGIN
  77.         fOldStyles := GetStylScrap(fHTE);
  78.         FailNIL(fOldStyles);
  79.         fStylePad := GetHandleSize(Handle(fOldStyles));
  80.         END;
  81.  
  82.     Success(fi);
  83.  
  84.     END;
  85.  
  86. {--------------------------------------------------------------------------------------------------}
  87. {$S TEDoCommand}
  88.  
  89. PROCEDURE TTECommand.Free; OVERRIDE;
  90.  
  91.     BEGIN
  92.     fOldText := DisposeIfHandle(fOldText);
  93.     Handle(fOldStyles) := DisposeIfHandle(fOldStyles);
  94.     fNewText := DisposeIfHandle(fNewText);
  95.     Handle(fNewStyles) := DisposeIfHandle(fNewStyles);
  96.     fPadding := DisposeIfHandle(fPadding);
  97.  
  98.     INHERITED Free;
  99.     END;
  100.  
  101. {--------------------------------------------------------------------------------------------------}
  102. {$S TEDoCommand}
  103.  
  104. PROCEDURE TTECommand.BanishOldText;
  105.  
  106.     BEGIN
  107.     IF fOldEnd > fOldStart THEN
  108.         TEDelete(fHTE);
  109.     SetHandleSize(fPadding, MAX( - (fTextPad + fStylePad), 0));
  110.     FailMemError;
  111.     END;
  112.  
  113. {--------------------------------------------------------------------------------------------------}
  114. {$S TEDoCommand}
  115.  
  116. PROCEDURE TTECommand.InstallNewText;
  117.  
  118.     VAR
  119.         savedSize:            LONGINT;
  120.         itsText:            Handle;
  121.  
  122.     BEGIN
  123.     IF fNewEnd > fNewStart THEN
  124.         BEGIN
  125.         itsText := fTEView.fText;
  126.         savedSize := GetHandleSize(itsText);
  127.  
  128.         {$IFC qDebug}
  129.         IF fNewText = NIL THEN
  130.             ProgramBreak('InstallNewText called with fNewText = NIL!');
  131.         {$ENDC}
  132.  
  133.         LockHandleHigh(fNewText);                                { Prevent heap fragmentation for TEInsert }
  134.  
  135.         IF fTEView.fStyleType = kWithStyle THEN         { If record has style, use it }
  136.             TEStylInsert(fNewText^,                     { It's okay for fNewStyles to be NIL here }
  137.                          GetHandleSize(fNewText), fNewStyles, fHTE)
  138.         ELSE                                            { Otherwise, do it the old-fashioned way }
  139.             TEInsert(fNewText^, GetHandleSize(fNewText), fHTE);
  140.  
  141.         HUnlock(fNewText);
  142.  
  143.         IF GetHandleSize(itsText) <= savedSize THEN
  144.             FailOSErr(memFullErr);
  145.  
  146.         fTEView.fSpecsChanged := TRUE;
  147.         END;
  148.     END;
  149.  
  150. {--------------------------------------------------------------------------------------------------}
  151. {$S TEFields}
  152.  
  153. PROCEDURE TTECommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  154.                                                 fieldAddr: Ptr;
  155.                                                 fieldType: INTEGER)); OVERRIDE;
  156.  
  157.     BEGIN
  158.     DoToField('TTECommand', NIL, bClass);
  159.     DoToField('fTEView', @fTEView, bObject);
  160.     DoToField('fHTE', @fHTE, bTEHandle);
  161.     DoToField('fOldStart', @fOldStart, bInteger);
  162.     DoToField('fOldEnd', @fOldEnd, bInteger);
  163.     DoToField('fOldText', @fOldText, bHandle);
  164.     DoToField('fOldStyles', @fOldStyles, bHandle);
  165.     DoToField('fNewStart', @fNewStart, bInteger);
  166.     DoToField('fNewEnd', @fNewEnd, bInteger);
  167.     DoToField('fNewText', @fNewText, bHandle);
  168.     DoToField('fNewStyles', @fNewStyles, bHandle);
  169.     DoToField('fPadding', @fPadding, bHandle);
  170.     DoToField('fTextPad', @fTextPad, bInteger);
  171.     DoToField('fStylePad', @fStylePad, bLongInt);
  172.     INHERITED Fields(DoToField);
  173.     END;
  174.  
  175. {--------------------------------------------------------------------------------------------------}
  176. {$S TEDoCommand}
  177.  
  178. PROCEDURE TTECommand.RemoveAdditions;
  179.  
  180.     BEGIN
  181.     IF fNewText <> NIL THEN
  182.         BEGIN
  183.         TESetSelect(fNewStart, fNewEnd, fHTE);
  184.         TEDelete(fHTE);
  185.         END;
  186.     SetHandleSize(fPadding, MAX(fTextPad + fStylePad, 0));
  187.     FailMemError;
  188.     END;
  189.  
  190. {--------------------------------------------------------------------------------------------------}
  191. {$S TEDoCommand}
  192.  
  193. PROCEDURE TTECommand.RestoreSelection;
  194.  
  195.     BEGIN
  196.     TESetSelect(fOldStart, fOldEnd, fHTE);
  197.     END;
  198.  
  199. {--------------------------------------------------------------------------------------------------}
  200. {$S TEDoCommand}
  201.  
  202. PROCEDURE TTECommand.ReviveDeletions;
  203.  
  204.     VAR
  205.         itsText:            Handle;
  206.         savedSize:            LONGINT;
  207.         nChars:             INTEGER;
  208.  
  209.     BEGIN
  210.     TESetSelect(fOldStart, fOldStart, fHTE);            { so insert will take place at right point }
  211.     nChars := GetHandleSize(fOldText);
  212.     IF nChars > 0 THEN
  213.         BEGIN
  214.         itsText := fTEView.fText;
  215.         savedSize := GetHandleSize(itsText);
  216.  
  217.         LockHandleHigh(fOldText);                                { Prevent heap fragmentation }
  218.  
  219.         IF fTEView.fStyleType = kWithStyle THEN         { If record has style, use it }
  220.             TEStylInsert(fOldText^, nChars,             { It's okay for fOldStyles to be NIL here }
  221.                          fOldStyles, fHTE)
  222.         ELSE                                            { Otherwise, do it the old-fashioned way }
  223.             TEInsert(fOldText^, nChars, fHTE);
  224.  
  225.         HUnlock(fOldText);
  226.  
  227.         IF GetHandleSize(itsText) <= savedSize THEN
  228.             FailOSErr(memFullErr);
  229.  
  230.         fTEView.fSpecsChanged := TRUE;
  231.         END;
  232.     END;
  233.  
  234. {--------------------------------------------------------------------------------------------------}
  235. {$S TEDoCommand}
  236.  
  237. PROCEDURE TTECommand.DoMainFunction;
  238.  
  239.     BEGIN
  240.     IF fCmdNumber <> cCopy THEN
  241.         BanishOldText;
  242.     InstallNewText;
  243.     IF fCmdNumber <> cCopy THEN
  244.         fTEView.SynchView(kRedraw);
  245.     END;
  246.  
  247. {--------------------------------------------------------------------------------------------------}
  248. {$S TEDoCommand}
  249.  
  250. PROCEDURE TTECommand.DoIt; OVERRIDE;
  251.  
  252.     BEGIN
  253.     IF fTEView.Focus THEN;                                {??? What if Focus fails}
  254.  
  255.     DoMainFunction;
  256.     {$IFC qDebug}
  257.     IF pTEIntenseDebugging THEN
  258.         DumpTTECommand(SELF);
  259.     {$ENDC}
  260.     END;
  261.  
  262. {--------------------------------------------------------------------------------------------------}
  263. {$S TEDoCommand}
  264.  
  265. PROCEDURE TTECommand.UndoIt; OVERRIDE;
  266.  
  267.     BEGIN
  268.     IF fTEView.Focus THEN;                                {??? What if Focus fails}
  269.  
  270.     RemoveAdditions;
  271.     ReviveDeletions;
  272.     RestoreSelection;
  273.     IF fCmdNumber <> cCopy THEN
  274.         fTEView.SynchView(kRedraw);
  275.     {$IFC qDebug}
  276.     IF pTEIntenseDebugging THEN
  277.         DumpTTECommand(SELF);
  278.     {$ENDC}
  279.     END;
  280.  
  281. {--------------------------------------------------------------------------------------------------}
  282. {$S TEDoCommand}
  283.  
  284. PROCEDURE TTECommand.RedoIt; OVERRIDE;
  285.  
  286.     BEGIN
  287.     IF fTEView.Focus THEN;                                {??? What if Focus fails}
  288.  
  289.     RestoreSelection;
  290.     DoMainFunction;
  291.     {$IFC qDebug}
  292.     IF pTEIntenseDebugging THEN
  293.         DumpTTECommand(SELF);
  294.     {$ENDC}
  295.     END;
  296.  
  297. {--------------------------------------------------------------------------------------------------}
  298. {$S TESelCommand}
  299.  
  300. PROCEDURE TTECutCopyCommand.ITECutCopyCommand(itsTEView: TTEView;
  301.                                               itsCmdNumber: CmdNumber);
  302.  
  303.     BEGIN
  304.     fClipCreated := FALSE;
  305.     ITECommand(itsTEView, itsCmdNumber, TRUE);
  306.     fChangesClipboard := TRUE;
  307.     fCausesChange := itsCmdNumber <> cCopy;
  308.     END;
  309.  
  310. {--------------------------------------------------------------------------------------------------}
  311. {$S TEDoCommand}
  312.  
  313. PROCEDURE TTECutCopyCommand.Free; OVERRIDE;
  314.  
  315.     BEGIN
  316.     IF fClipCreated THEN
  317.         fOldText := NIL;
  318.     INHERITED Free;
  319.     END;
  320.  
  321. {--------------------------------------------------------------------------------------------------}
  322. {$S TEDoCommand}
  323.  
  324. PROCEDURE TTECutCopyCommand.DoIt; OVERRIDE;
  325.  
  326.     VAR
  327.         clipTEView:         TTEView;
  328.         clipHere:            BOOLEAN;
  329.         fi:                 FailInfo;
  330.         clipStyle:            TextStyle;
  331.         itsSize:            VPoint;
  332.         itsMargins:         Rect;
  333.  
  334.     PROCEDURE HdlClipFailed(error: OSErr;
  335.                             message: LONGINT);
  336.  
  337.         BEGIN
  338.         FreeIfObject(clipTEView);
  339.         clipTEView := NIL;
  340.         END;
  341.  
  342.     BEGIN                                                {TTECutCopyCommand.DoIt}
  343.     IF fTEView.Focus THEN;                                {??? What if Focus fails}
  344.  
  345.     SetTextStyle(clipStyle, applFont, [],                { Initial style same as virgin TEView }
  346.                  12, gRGBBlack);
  347.  
  348.     SetVPt(itsSize, 100, 50);                            { An arbitrary initial size. }
  349.     SetRect(itsMargins, 10, 8, 10, 0);                    { No bottom margin. }
  350.  
  351.     New(clipTEView);                                    { Create a new view for the clipboard }
  352.     FailNIL(clipTEView);
  353.     WITH fTEView DO
  354.         clipTEView.ITEView(NIL, NIL,                    { Initialize view }
  355.                            gZeroVPt, itsSize, sizeSuperView, sizeVariable, itsMargins, clipStyle,
  356.                            teJustSystem, fStyleType, fAutoWrap);
  357.     clipTEView.fAcceptsChanges := FALSE;                { This is a read-only view }
  358.  
  359.     CatchFailures(fi, HdlClipFailed);                    { Cut can eat into temp memory so users can
  360.                                                          }
  361.     { …rescue text from overweight documents }
  362.     IF NOT fCausesChange THEN                            { If Copy-ing, assure there's enough room }
  363.         FailSpaceIsLow;
  364.     Success(fi);
  365.     clipTEView.StuffText(fOldText);
  366.     FailSpaceIsLow;
  367.  
  368.     {??? GOT TO FIGURE OUT SOME WAY TO PRE-FLIGHT THIS! ??????????????????????????????????? }
  369.     IF clipTEView.fStyleType = kWithStyle THEN            { If record has style }
  370.         SetStylScrap(0, MAXINT, fOldStyles,             { …then put in the styles }
  371.                      kDontRedraw, clipTEView.fHTE);
  372.     FailSpaceIsLow;
  373.  
  374.     clipTEView.fFreeText := TRUE;                        { Let TEView know it has to free the text }
  375.  
  376.     gApplication.ClaimClipboard(clipTEView);            { Okay to claim (will call RecalcText!) }
  377.  
  378.     fClipCreated := TRUE;                                { We be done }
  379.     DoMainFunction;                                     { Do the actual cut/copy }
  380.  
  381.     {$IFC qDebug}
  382.     IF pTEIntenseDebugging THEN
  383.         BEGIN
  384.         DumpTERecord(clipTEView.fHTE);
  385.         DumpTTECommand(SELF);
  386.         END;
  387.     {$ENDC}
  388.     END;
  389.  
  390. {--------------------------------------------------------------------------------------------------}
  391. {$S TEDoCommand}
  392.  
  393. PROCEDURE TTECutCopyCommand.ReviveDeletions; OVERRIDE;
  394.  
  395.     BEGIN
  396.     IF fCmdNumber = cCut THEN
  397.         INHERITED ReviveDeletions;                        { Don't do it for COPY }
  398.     END;
  399.  
  400. {--------------------------------------------------------------------------------------------------}
  401. {$S TEFields}
  402.  
  403. PROCEDURE TTECutCopyCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  404.                                                        fieldAddr: Ptr;
  405.                                                        fieldType: INTEGER)); OVERRIDE;
  406.  
  407.     BEGIN
  408.     DoToField('TTECutCopyCommand', NIL, bClass);
  409.     DoToField('fClipCreated', @fClipCreated, bBoolean);
  410.     INHERITED Fields(DoToField);
  411.     END;
  412.  
  413. {--------------------------------------------------------------------------------------------------}
  414. {$S TESelCommand}
  415.  
  416. PROCEDURE TTEPasteCommand.ITEPasteCommand(itsTEView: TTEView);
  417. { We can't use TEPaste because it clobbers the DeskScrap; the text would be recoverable
  418.   from the special TextEdit Scrap, but other types of non-TEXT scrap are permanently
  419.   lost, it seems }
  420.  
  421.     VAR
  422.         savedPerm:            BOOLEAN;
  423.         newLength:            INTEGER;
  424.         newStyleLen:        LONGINT;
  425.         newText:            Handle;
  426.         newStyles:            StScrpHandle;
  427.         dataType:            ResType;
  428.         fi:                 FailInfo;
  429.  
  430.     PROCEDURE HdlPasteFailed(error: OSErr;
  431.                              message: LONGINT);
  432.  
  433.         BEGIN
  434.         IF newText <> fNewText THEN                     { newText is assigned to fNewText }
  435.             newText := DisposeIfHandle(newText);        { …so avoid disposing twice. }
  436.         IF newStyles <> fNewStyles THEN                 { Ditto for newStyles. }
  437.             Handle(newStyles) := DisposeIfHandle(newStyles);
  438.         Free;
  439.         END;
  440.  
  441.     BEGIN
  442.     ITECommand(itsTEView, cPaste, TRUE);                { Perform stock initializations }
  443.  
  444.     savedPerm := FALSE;
  445.  
  446.     newStyleLen := 0;                                    { Assume there are no new styles }
  447.     newStyles := NIL;
  448.     newText := NIL;
  449.  
  450.     CatchFailures(fi, HdlPasteFailed);
  451.  
  452.     newText := NewPermHandle(0);                        { Create handle to receive clipboard data }
  453.     FailNIL(newText);
  454.     IF itsTEView.fStyleType = kWithStyle THEN
  455.         BEGIN
  456.         newStyles := StScrpHandle(NewPermHandle(0));    { Same for handle to receive style info }
  457.         FailNIL(newStyles);
  458.         END;
  459.  
  460.     newLength := gApplication.GetDataToPaste(newText, dataType);
  461.  
  462.     IF newLength > 0 THEN
  463.         BEGIN
  464.         {$IFC qDebug}
  465.         IF dataType <> 'TEXT' THEN
  466.             ProgramBreak('TEPasteCommand given some non-text from clipboard')
  467.         ELSE
  468.         {$ENDC}
  469.             BEGIN                                        { Prime "new" values }
  470.             fNewText := newText;
  471.             fNewStart := fHTE^^.selStart;
  472.             fNewEnd := fNewStart + newLength;
  473.             fTextPad := newLength - (fOldEnd - fOldStart);
  474.  
  475.             IF itsTEView.fStyleType = kWithStyle THEN
  476.                 BEGIN
  477.                 newStyleLen := gClipView.GivePasteData(Handle(newStyles), 'styl');
  478.                 IF newStyleLen > 0 THEN
  479.                     BEGIN
  480.                     fNewStyles := newStyles;
  481.                     fStylePad :=                        { Difference between old and new styles }
  482.                       newStyleLen - fStylePad;
  483.                     END;
  484.                 END;
  485.  
  486.             SetPermHandleSize(fPadding, MAX(fTextPad + fStylePad, 0));
  487.  
  488.             FailSpaceIsLow;
  489.             END;
  490.         END
  491.     ELSE
  492.         BEGIN
  493.         newText := DisposeIfHandle(newText);
  494.         Handle(newStyles) := DisposeIfHandle(newStyles);
  495.         END;
  496.     Success(fi);
  497.     END;
  498.  
  499. {--------------------------------------------------------------------------------------------------}
  500. {$S TEFields}
  501.  
  502. PROCEDURE TTEPasteCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  503.                                                      fieldAddr: Ptr;
  504.                                                      fieldType: INTEGER)); OVERRIDE;
  505.  
  506.     BEGIN
  507.     DoToField('TTEPasteCommand', NIL, bClass);
  508.     INHERITED Fields(DoToField);
  509.     END;
  510.  
  511. {--------------------------------------------------------------------------------------------------}
  512. {$S TESelCommand}
  513.  
  514. PROCEDURE TTEStyleCommand.ITEStyleCommand(itsTEView: TTEView;
  515.                                           itsNewStyle: TextStyle;
  516.                                           itsCmdNumber: CmdNumber;
  517.                                           itsMode: INTEGER);
  518.  
  519.     VAR
  520.         savedPerm:            BOOLEAN;
  521.         fi:                 FailInfo;
  522.  
  523.     BEGIN
  524.  
  525.     ITECommand(itsTEView, itsCmdNumber, FALSE);         { Perform stock initialization, sans text }
  526.  
  527.     fOldTextStyle := itsTEView.fTextStyle;
  528.     fNewTextStyle := itsNewStyle;
  529.  
  530.     { Only do color change if we can }
  531.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  532.         fMode := itsMode
  533.     ELSE
  534.         fMode := BAND(itsMode, BNOT(doColor));
  535.     END;
  536.  
  537. {--------------------------------------------------------------------------------------------------}
  538. {$S TEDoCommand}
  539.  
  540. PROCEDURE TTEStyleCommand.InstallOneStyle(newStyl: TextStyle);
  541.  
  542.     BEGIN
  543.     fTEView.SetOneStyle(fOldStart, fOldEnd, fMode, newStyl, kRedraw); { Focus'es for us }
  544.     END;
  545.  
  546. {--------------------------------------------------------------------------------------------------}
  547. {$S TEDoCommand}
  548.  
  549. PROCEDURE TTEStyleCommand.InstallManyStyles(newStyls: StScrpHandle);
  550.  
  551.     BEGIN
  552.     IF fTEView.Focus THEN;
  553.     { No need to check for fStyleType, since we only get here if the record is stylish }
  554.     SetStylScrap(fOldStart, fOldEnd, newStyls, kRedraw, fHTE);
  555.     fTEView.RecalcText;                                 { Might have changed number of lines }
  556.     fTEView.SynchView(kRedraw);                         { Show corrected view }
  557.  
  558.     fTEView.fSpecsChanged := TRUE;
  559.     END;
  560.  
  561. {--------------------------------------------------------------------------------------------------}
  562. {$S TEDoCommand}
  563.  
  564. PROCEDURE TTEStyleCommand.DoIt; OVERRIDE;
  565.  
  566.     VAR
  567.         aTextStyle:         TextStyle;
  568.  
  569.     BEGIN
  570.     aTextStyle := fNewTextStyle;
  571.     InstallOneStyle(aTextStyle);
  572.     fMode := BAND(fMode, BNOT(doToggle));                { Turn off toggle mode, if set }
  573.     {$IFC qDebug}
  574.     IF pTEIntenseDebugging THEN
  575.         DumpTTECommand(SELF);
  576.     {$ENDC}
  577.     END;
  578.  
  579. {--------------------------------------------------------------------------------------------------}
  580. {$S TEDoCommand}
  581.  
  582. PROCEDURE TTEStyleCommand.UndoIt; OVERRIDE;
  583.  
  584.     VAR
  585.         aTextStyle:         TextStyle;
  586.  
  587.     BEGIN
  588.     RestoreSelection;
  589.  
  590.     IF fTEView.fStyleType = kWithoutStyle THEN
  591.         BEGIN
  592.         aTextStyle := fOldTextStyle;
  593.         InstallOneStyle(aTextStyle);
  594.         END
  595.     ELSE
  596.         InstallManyStyles(fOldStyles);
  597.     {$IFC qDebug}
  598.     IF pTEIntenseDebugging THEN
  599.         DumpTTECommand(SELF);
  600.     {$ENDC}
  601.     END;
  602.  
  603. {--------------------------------------------------------------------------------------------------}
  604. {$S TEDoCommand}
  605.  
  606. PROCEDURE TTEStyleCommand.RedoIt; OVERRIDE;
  607.  
  608.     BEGIN
  609.     RestoreSelection;
  610.     DoIt;
  611.     END;
  612.  
  613. {--------------------------------------------------------------------------------------------------}
  614. {$S TEFields}
  615.  
  616. PROCEDURE TTEStyleCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  617.                                                      fieldAddr: Ptr;
  618.                                                      fieldType: INTEGER)); OVERRIDE;
  619.  
  620.     BEGIN
  621.     DoToField('TTEStyleCommand', NIL, bClass);
  622.     DoToField('fMode', @fMode, bInteger);
  623.     {$Push} {$H-}
  624.     TextStyleFields('fOldTextStyle', fOldTextStyle, DoToField);
  625.     TextStyleFields('fNewTextStyle', fNewTextStyle, DoToField);
  626.     {$Pop}
  627.     INHERITED Fields(DoToField);
  628.     END;
  629.  
  630. {--------------------------------------------------------------------------------------------------}
  631. {$S TERes}
  632.  
  633. PROCEDURE TTETypingCommand.ITETypingCommand(itsTEView: TTEView;
  634.                                             itsFirstChar: Char);
  635.  
  636.     VAR
  637.         fi:                 FailInfo;
  638.  
  639.     PROCEDURE HdlInitFailed(error: OSErr;
  640.                             message: LONGINT);
  641.  
  642.         BEGIN
  643.         Free;
  644.         END;
  645.  
  646.     BEGIN
  647.     ITECommand(itsTEView, cTyping, TRUE);
  648.  
  649.     CatchFailures(fi, HdlInitFailed);
  650.  
  651.     fNewStart := fHTE^^.selStart;                        { Start and end are the same }
  652.     fNewEnd := fNewStart;
  653.  
  654.     fNewText := NewPermHandle(0);                        { Allocate an empty block for text }
  655.     FailNIL(fNewText);
  656.  
  657.     fCompleted := FALSE;                                { We've only just begun… }
  658.     fFirstChar := itsFirstChar;                         { Save character for Doit }
  659.     Success(fi);
  660.     END;
  661.  
  662. {--------------------------------------------------------------------------------------------------}
  663. {$S TERes}
  664.  
  665. PROCEDURE TTETypingCommand.Free; OVERRIDE;
  666.  
  667.     BEGIN
  668.     IF fTEView.fTypingCommand = SELF THEN
  669.         fTEView.fTypingCommand := NIL;
  670.     INHERITED Free;
  671.     END;
  672.  
  673. {--------------------------------------------------------------------------------------------------}
  674. {$S TERes}
  675.  
  676. PROCEDURE TTETypingCommand.DoNormalChar(aChar: Char);
  677.  
  678.     BEGIN
  679.     FailOSErr(PtrAndHand(Ptr(SUCC(ORD(@aChar))),        { Append char to end of fNewText }
  680.                          fNewText, 1));
  681.     fNewEnd := SUCC(fNewEnd);                            { Bump both end of "selection" }
  682.     fTextPad := SUCC(fTextPad);                         { …and padding value }
  683.  
  684.     SetHandleSize(fPadding,                             { This SetHandleSize can't grow the handle,
  685.                                                          }
  686.                   MAX( - (fTextPad + fStylePad), 0));    { …so it shouldn't fail. }
  687.     FailMemError;
  688.     END;
  689.  
  690. {--------------------------------------------------------------------------------------------------}
  691. { User has backspaced to the left of the original starting point.  First, copy the
  692.  character (which may be more than one byte long if we are using a non-Roman script)
  693.  to a temporary buffer.  The assumption is that no character will ever be longer
  694.  than four bytes.  Sorry, folks, MacApp does not support typing in any script with
  695.  more than 4 billion characters.
  696.   Next, copy the character to the front of fOldText, and adjust fOldStart, fNewStart,
  697.  and fNewEnd.  Note that we do NOT check for MemSpaceIsLow, since we want to let the
  698.  user delete characters. }
  699. {$S TERes}
  700.  
  701. PROCEDURE TTETypingCommand.BkSpcLeft(theText: Handle;
  702.                                      curStart: INTEGER);
  703.  
  704.     TYPE
  705.         TSPtr                = ^TextStyle;
  706.  
  707.     VAR
  708.         savedSize:            INTEGER;
  709.         theHeight:            INTEGER;
  710.         theAscent:            INTEGER;
  711.         oldSize:            LONGINT;
  712.         whoCares:            LONGINT;
  713.         aTextStyle:         TSPtr;
  714.         savedChar:            PACKED ARRAY [0..3] OF Char;
  715.         delStyle:            TextStyle;
  716.         {$IFC qDebug}
  717.         savedPerm:            BOOLEAN;
  718.         {$ENDC}
  719.  
  720.     BEGIN
  721.     savedSize := 1;
  722.     IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  723.         WHILE CharByte(theText^, curStart - savedSize) > 0 DO
  724.             savedSize := SUCC(savedSize);
  725.     curStart := curStart - savedSize;
  726.  
  727.     {$IFC qDebug}
  728.     IF savedSize > 4 THEN
  729.         ProgramBreak('Character > 4 bytes');
  730.     {$ENDC}
  731.     IF savedSize = 1 THEN                                { Slight speed optimization for normal case
  732.                                                          }
  733.     {$Push} {$R-}
  734.         savedChar[0] := CharsHandle(theText)^^[curStart]
  735.         {$Pop}
  736.     ELSE
  737.         BlockMove(Ptr(ORD(theText^) + curStart), @savedChar, savedSize);
  738.  
  739.     IF fTEView.fStyleType = kWithStyle THEN             { Only do this if styles are around }
  740.         BEGIN
  741.         TEGetStyle(curStart, delStyle,                    { Get the style of the deleted character }
  742.                    theHeight, theAscent, fHTE);         { (1 or 4 bytes, it's all only one style) }
  743.  
  744.         IF NOT EqualBlocks(@delStyle,                    { If style doesn't match first in the list }
  745.                            @fOldStyles^^.scrpStyleTab[0].scrpFont, SIZEOF(TextStyle)) THEN
  746.             BEGIN                                        { …then insert new style at head of list }
  747.             fTEView.fSpecsChanged := TRUE;                { User backspaced into new style! }
  748.  
  749.             oldSize :=                                    { Make room for the new style element }
  750.               GetHandleSize(Handle(fOldStyles));
  751.             SetHandleSize(Handle(fOldStyles), oldSize + SIZEOF(ScrpSTElement));
  752.             FailMemError;
  753.             fStylePad := fStylePad + SIZEOF(ScrpSTElement);
  754.  
  755.             {$Push} {$H-}
  756.             WITH fOldStyles^^.scrpStyleTab[0] DO
  757.                 BlockMove(@scrpStartChar,                { Move entire array up one element's size }
  758.                           Ptr(ORD(@scrpStartChar) + SIZEOF(ScrpSTElement)), oldSize -
  759.                           SIZEOF(fOldStyles^^.scrpNStyles));
  760.             {$Pop}
  761.  
  762.             fOldStyles^^.scrpNStyles :=                 { One more style }
  763.               SUCC(fOldStyles^^.scrpNStyles);
  764.             WITH fOldStyles^^.scrpStyleTab[0] DO
  765.                 BEGIN
  766.                 scrpHeight := theHeight;                { Fill in the blanks }
  767.                 scrpAscent := theAscent;
  768.                 aTextStyle := TSPtr(@scrpFont);
  769.                 aTextStyle^ := delStyle;
  770.                 END;
  771.             END;
  772.  
  773.         WITH fOldStyles^^.scrpStyleTab[0] DO
  774.             scrpStartChar := PRED(scrpStartChar);        { Regardless, back off offset by one }
  775.         END;
  776.  
  777.     SetHandleSize(fPadding, GetHandleSize(fOldText) + savedSize + fStylePad);
  778.     FailMemError;
  779.     whoCares := Munger(fOldText, 0, NIL, 0, @savedChar, savedSize);
  780.     FailMemError;
  781.     fOldStart := curStart;                                { Treat this as though original selection }
  782.     fNewStart := curStart;                                { …had included this character }
  783.     fNewEnd := curStart;
  784.     fTextPad := fTextPad - savedSize;
  785.     END;
  786.  
  787. {--------------------------------------------------------------------------------------------------}
  788. {$S TERes}
  789.  
  790. PROCEDURE TTETypingCommand.BkSpcRight(theText: Handle;
  791.                                       curStart: INTEGER);
  792.  
  793.     VAR
  794.         savedSize:            INTEGER;
  795.  
  796.     BEGIN
  797.     savedSize := 1;
  798.     IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  799.         WHILE CharByte(theText^, curStart - savedSize) > 0 DO
  800.             savedSize := SUCC(savedSize);
  801.     SetHandleSize(fPadding, MAX( - (fTextPad - savedSize + fStylePad), 0));
  802.     FailMemError;
  803.     fNewEnd := fNewEnd - savedSize;
  804.     fTextPad := fTextPad - savedSize;
  805.  
  806.     SetHandleSize(fNewText, fNewEnd - fNewStart);        { Shouldn't fail as we're only shrinking it
  807.                                                          }
  808.     FailMemError;
  809.     END;
  810.  
  811. {--------------------------------------------------------------------------------------------------}
  812. { Forward delete courtesy of: Larry Goldman.  Used by permission. }
  813. {$S TERes}
  814.  
  815. PROCEDURE TTETypingCommand.FwdDelete(theText: Handle;
  816.                                      curStart, curEnd: INTEGER);
  817.  
  818.     TYPE
  819.         TSPtr                = ^TextStyle;
  820.  
  821.     VAR
  822.         savedSize:            INTEGER;
  823.         theHeight:            INTEGER;
  824.         theAscent:            INTEGER;
  825.         oldSize:            LONGINT;
  826.         whoCares:            LONGINT;
  827.         aTextStyle:         TSPtr;
  828.         savedChar:            PACKED ARRAY [0..3] OF Char;
  829.         delStyle:            TextStyle;
  830.         textSize:            LONGINT;
  831.         oldTextSize:        LONGINT;
  832.  
  833.     BEGIN
  834.     textSize := GetHandleSize(theText);
  835.     IF (curStart = curEnd) & (curStart < textSize) THEN
  836.         BEGIN
  837.  
  838.         savedSize := 0;                                 {Get the complete character}
  839.         IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  840.             WHILE (curStart + savedSize <= textSize) & (CharByte(theText^, curStart + savedSize) >
  841.                   0) DO
  842.                 savedSize := SUCC(savedSize);
  843.         savedSize := savedSize + 1;
  844.         {$IFC qDebug}
  845.         IF savedSize > 4 THEN
  846.             ProgramBreak('Character > 4 bytes');
  847.         {$ENDC}
  848.  
  849.         IF savedSize = 1 THEN                            { Slight speed optimization for normal case
  850.                                                          }
  851.         {$Push} {$R-}
  852.             savedChar[0] := CharsHandle(theText)^^[curStart]
  853.             {$Pop}
  854.         ELSE
  855.             BlockMove(Ptr(ORD(theText^) + curStart), @savedChar, savedSize);
  856.  
  857.         IF (curStart >= fNewStart) & (curStart < fNewEnd) THEN { char is within fNewText }
  858.             BEGIN                                        {Remove the char from fNewText and update
  859.                                                          fNewEnd and fTextPad}
  860.             SetHandleSize(fPadding, MAX( - (fTextPad - savedSize + fStylePad), 0));
  861.             FailMemError;
  862.             fNewEnd := fNewEnd - savedSize;
  863.             fTextPad := fTextPad - savedSize;
  864.  
  865.             { Shouldn't fail as we're only shrinking it }
  866.             whoCares := Munger(fNewText, curStart - fNewStart, NIL, savedSize, @savedChar, 0);
  867.             FailMemError;
  868.             END
  869.         ELSE                                            { add char to the end of fOldChars, don't
  870.                                                          update fOldEnd, but update fPadding}
  871.             BEGIN
  872.             oldTextSize := GetHandleSize(fOldText);
  873.             IF fTEView.fStyleType = kWithStyle THEN     { Only do this if styles are around }
  874.                 BEGIN
  875.                 TEGetStyle(curStart, delStyle,            { Get the style of the deleted character }
  876.                            theHeight, theAscent, fHTE); { (1 or 4 bytes, it's all only one style) }
  877.  
  878.                 IF NOT EqualBlocks(@delStyle,            { If style doesn't match last in the list }
  879.                                    @fOldStyles^^.scrpStyleTab[fOldStyles^^.scrpNStyles -
  880.                                    1].scrpFont, SIZEOF(TextStyle)) THEN
  881.                     BEGIN                                { …then insert new style at end of list }
  882.                     fTEView.fSpecsChanged := TRUE;        { User backspaced into new style! }
  883.  
  884.                     oldSize :=                            { Make room for the new style element }
  885.                       GetHandleSize(Handle(fOldStyles));
  886.                     SetHandleSize(Handle(fOldStyles), oldSize + SIZEOF(ScrpSTElement));
  887.                     FailMemError;
  888.                     fStylePad := fStylePad + SIZEOF(ScrpSTElement);
  889.  
  890.                     fOldStyles^^.scrpNStyles :=         { One more style }
  891.                       SUCC(fOldStyles^^.scrpNStyles);
  892.                     WITH fOldStyles^^.scrpStyleTab[fOldStyles^^.scrpNStyles - 1] DO
  893.                         BEGIN
  894.                         scrpStartChar := oldTextSize;
  895.                         scrpHeight := theHeight;        { Fill in the blanks }
  896.                         scrpAscent := theAscent;
  897.                         aTextStyle := TSPtr(@scrpFont);
  898.                         aTextStyle^ := delStyle;
  899.                         END;
  900.                     END;
  901.                 END;
  902.  
  903.             SetHandleSize(fPadding, oldTextSize + savedSize + fStylePad);
  904.             FailMemError;
  905.             whoCares := Munger(fOldText, oldTextSize, NIL, 0, @savedChar, savedSize);
  906.             FailMemError;
  907.             fTextPad := fTextPad - savedSize;
  908.  
  909.             END;
  910.         END;
  911.     END;
  912.  
  913. {--------------------------------------------------------------------------------------------------}
  914. { ??? All this handle munging is expensive.  Better would be to accumulate memory in
  915.   "chunks" of, say, 16 bytes so that this checking need not happen every time through.
  916.   Fortunately, the normal cases are not that bad. }
  917. {$S TERes}
  918.  
  919. PROCEDURE TTETypingCommand.AddCharacter(aChar: Char);
  920.  
  921.     VAR
  922.         theText:            Handle;
  923.         curSelStart:        INTEGER;
  924.         curSelEnd:            INTEGER;
  925.         savedPerm:            BOOLEAN;
  926.         fi:                 FailInfo;
  927.         index:                INTEGER;
  928.  
  929.     PROCEDURE HdlCharFailed(error: OSErr;
  930.                             message: LONGINT);
  931.  
  932.         BEGIN
  933.         savedPerm := PermAllocation(savedPerm);
  934.         END;
  935.  
  936.     BEGIN
  937.     fView.Update;                                        { Makes sure that all of TE's actions are
  938.                                                          Visible }
  939.     IF fView.Focus THEN;
  940.     WITH fHTE^^ DO                                        { Get handy info about the text handle }
  941.         BEGIN
  942.         curSelStart := selStart;
  943.         curSelEnd := selEnd;
  944.         theText := hText;
  945.         END;
  946.     CatchFailures(fi, HdlCharFailed);
  947.     savedPerm := PermAllocation(TRUE);
  948.  
  949.  { Update the fNewText handle and other information.  Note that because of backspace,
  950.   this can be tricky.}
  951.  
  952.     IF (aChar = chFwdDelete) THEN
  953.         FwdDelete(theText, curSelStart, curSelEnd)        { User types forward delete, so keep in
  954.                                                          synch}
  955.  
  956.     ELSE IF aChar <> chBackspace THEN                    { Not a backspace. Do the right thing }
  957.         DoNormalChar(aChar)
  958.  
  959.     ELSE IF (curSelStart <= fOldStart) &                { User typed backspace so keep in synch }
  960.             (curSelStart > 0) & (curSelStart = curSelEnd) THEN
  961.         BkSpcLeft(theText, curSelStart)                 { Handle backspace to left of start }
  962.  
  963.     ELSE IF fNewEnd > fNewStart THEN                    { Delete 1 character from end of fNewText }
  964.         BkSpcRight(theText, curSelStart);                { Handle backspace to right of start }
  965.  
  966.     savedPerm := PermAllocation(savedPerm);
  967.     Success(fi);
  968.  
  969.     IF aChar <> chFwdDelete THEN
  970.  { Let TextEdit have the character, as either 1) we're adding a byte, so we know there
  971.   is a reserve tank, so the worst this will do is eat into it a little, or 2) we're
  972.   deleting a character, which can only decrease memory usage. }
  973.         TEKey(aChar, fHTE)
  974.     ELSE IF (curSelStart <> curSelEnd) THEN             { forward delete with chars selected}
  975.         TEDelete(fHTE)
  976.     ELSE IF (curSelStart < GetHandleSize(theText)) THEN
  977.         BEGIN                                            { forward delete with insertion point}
  978.         TEKey(chRight, fHTE);
  979.         TEKey(chBackspace, fHTE);
  980.         END;
  981.  
  982.     fTEView.SynchView(kRedraw);                         { Now clean up the view. }
  983.  
  984.     {$IFC qDebug}
  985.     IF pTEIntenseDebugging THEN
  986.         BEGIN
  987.         WrLblHandleContents('fOldText', fOldText);
  988.         WRITELN;
  989.         WrLblHandleContents('fNewText', fNewText);
  990.         WRITELN;
  991.         DumpTTECommand(SELF);
  992.         END;
  993.     {$ENDC}
  994.  
  995.     END;
  996.  
  997. {--------------------------------------------------------------------------------------------------}
  998. {$S TERes}
  999.  
  1000. PROCEDURE TTETypingCommand.DoIt; OVERRIDE;
  1001.  
  1002.     BEGIN
  1003.     AddCharacter(fFirstChar);
  1004.     {$IFC qDebug}
  1005.     IF pTEIntenseDebugging THEN
  1006.         DumpTTECommand(SELF);
  1007.     {$ENDC}
  1008.     END;
  1009.  
  1010. {--------------------------------------------------------------------------------------------------}
  1011.  
  1012. {$S TEDoCommand}
  1013.  
  1014. PROCEDURE TTETypingCommand.RedoIt; OVERRIDE;
  1015.  
  1016.     VAR
  1017.         currentStyle:        TextStyle;
  1018.         lineHeight:         INTEGER;
  1019.         fontAscent:         INTEGER;
  1020.         resetStyle:         BOOLEAN;
  1021.  
  1022.     BEGIN
  1023.     IF (fOldEnd - fOldStart) = GetHandleSize(fOldText) THEN
  1024.         BEGIN                                            { No chars were vacuumed}
  1025.         resetStyle := FALSE;
  1026.         IF (fTEView.fStyleType = kWithStyle) & (fOldEnd = fOldStart) THEN
  1027.             BEGIN
  1028.             TEGetStyle(fOldStart, currentStyle, lineHeight, fontAscent, fHTE);
  1029.             resetStyle := NOT EqualBlocks(@currentStyle, @fOldStyles^^.scrpStyleTab[0].scrpFont,
  1030.                                           SIZEOF(TextStyle));
  1031.             END;
  1032.  
  1033.         IF resetStyle THEN                                { The new text has a style of its own }
  1034.             fNewStyles := fOldStyles;                    { Make InstallNewText insert styles, too }
  1035.         INHERITED RedoIt;
  1036.         IF resetStyle THEN
  1037.             fNewStyles := NIL;                            { So fNewStyles doesn't get disposed }
  1038.         END
  1039.     ELSE
  1040.         BEGIN
  1041.         IF fTEView.Focus THEN;                            {??? What if Focus fails}
  1042.         TESetSelect(fOldStart, fOldStart + GetHandleSize(fOldText), fHTE); { select vacuumed chars,
  1043.             too }
  1044.         TEDelete(fHTE);                                 { Remove old text, including vacuumed chars}
  1045.         SetHandleSize(fPadding, MAX( - (fTextPad + fStylePad), 0));
  1046.         FailMemError;
  1047.         InstallNewText;
  1048.         fTEView.SynchView(kRedraw);
  1049.         {$IFC qDebug}
  1050.         IF pTEIntenseDebugging THEN
  1051.             DumpTTECommand(SELF);
  1052.         {$ENDC}
  1053.         END;
  1054.     END;
  1055.  
  1056. {--------------------------------------------------------------------------------------------------}
  1057. {$S TEDoCommand}
  1058.  
  1059. PROCEDURE TTETypingCommand.UndoIt; OVERRIDE;
  1060.  
  1061.     BEGIN
  1062.     CompleteTyping;
  1063.     INHERITED UndoIt;
  1064.     END;
  1065.  
  1066. {--------------------------------------------------------------------------------------------------}
  1067. {$S TERes}
  1068.  
  1069. PROCEDURE TTETypingCommand.CompleteTyping;
  1070.  
  1071.     VAR
  1072.         i:                    INTEGER;
  1073.         offset:             LONGINT;
  1074.  
  1075.     BEGIN
  1076.     fCompleted := TRUE;
  1077.  
  1078.     IF fTEView.fStyleType = kWithStyle THEN
  1079.         WITH fOldStyles^^ DO
  1080.             BEGIN
  1081.             offset := - scrpStyleTab[0].scrpStartChar;
  1082.             IF offset > 0 THEN
  1083.                 FOR i := 0 TO scrpNStyles - 1 DO
  1084.                     scrpStyleTab[i].scrpStartChar := scrpStyleTab[i].scrpStartChar + offset;
  1085.             END;
  1086.     {$IFC qDebug}
  1087.     IF pTEIntenseDebugging THEN
  1088.         DumpTTECommand(SELF);
  1089.     {$ENDC}
  1090.     END;
  1091.  
  1092. {--------------------------------------------------------------------------------------------------}
  1093. {$S TEFields}
  1094.  
  1095. PROCEDURE TTETypingCommand.Fields(PROCEDURE DoToField(fieldName: Str255;
  1096.                                                       fieldAddr: Ptr;
  1097.                                                       fieldType: INTEGER)); OVERRIDE;
  1098.  
  1099.     BEGIN
  1100.     DoToField('TTETypingCommand', NIL, bClass);
  1101.     DoToField('fCompleted', @fCompleted, bBoolean);
  1102.     DoToField('fFirstChar', @fFirstChar, bBoolean);
  1103.     INHERITED Fields(DoToField);
  1104.     END;
  1105.